package com.iwhalecloud.bss.kite.cucc.order.flow;


import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.iwhalecloud.bss.kite.common.callclient.util.ClazzUtil;
import com.iwhalecloud.bss.kite.common.constant.ErrorConsts;
import com.iwhalecloud.bss.kite.common.constant.KeyConsts;
import com.iwhalecloud.bss.kite.common.constant.OrderFlowConsts;
import com.iwhalecloud.bss.kite.common.util.KiteBeanUtils;
import com.iwhalecloud.bss.kite.common.util.KiteListUtils;
import com.iwhalecloud.bss.kite.common.util.KiteStringUtils;
import com.iwhalecloud.bss.kite.cucc.order.flow.handles.DefaultTacheHandler;
import com.iwhalecloud.bss.kite.cucc.order.flow.handles.TacheHandler;
import com.iwhalecloud.bss.kite.dataservice.cache.TacheTypeCache;
import com.iwhalecloud.bss.kite.dataservice.cache.TacheTypeRelationCache;
import com.iwhalecloud.bss.kite.dataservice.entity.OrderItem;
import com.iwhalecloud.bss.kite.dataservice.entity.flow.Flow;
import com.iwhalecloud.bss.kite.dataservice.entity.flow.LFlow;
import com.iwhalecloud.bss.kite.dataservice.entity.flow.TacheType;
import com.iwhalecloud.bss.kite.dataservice.entity.flow.TacheTypeRelation;
import com.iwhalecloud.bss.kite.dataservice.entity.inst.ProdInst;
import com.iwhalecloud.bss.kite.dataservice.mapper.flow.FlowMapper;
import com.iwhalecloud.bss.kite.dataservice.mapper.flow.LFlowMapper;
import com.iwhalecloud.bss.kite.dataservice.mapper.inst.ProdInstMapper;
import com.iwhalecloud.bss.kite.dataservice.util.IdUtils;
import com.ztesoft.bss.common.util.SpringUtil;
import com.ztesoft.bss.order.base.consts.KeyValues;
import com.ztesoft.common.logger.api.ZLogger;
import com.ztesoft.common.logger.factory.ZLoggerFactory;
import org.apache.commons.lang3.StringUtils;
import tk.mybatis.mapper.entity.Example;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @author yu.baocheng@iwhalecloud.com<br>
 * @version 1.0<br>
 * @taskId <br>
 * @CreateDate 2020/3/1 6:27 下午 <br>
 * @since ZSmart_BSS_COC_R3.1<br>
 * <Description> 订单流程处理引擎 <br>
 */
public final class FlowEngine {
    static ZLogger logger = ZLoggerFactory.getLogger(FlowEngine.class, KeyConsts.LOG_MODULE);

    private FlowEngine() {

    }

    public static String invokeTacheHander(Flow flow) {
        /*
         * 1.通过反射创建tacheHandler = this.getTachType(tacheTypeId).getTachHanlder();(使用Class.forName(tacheTypeId).newInstance())
         * 2.执行环节处理器String handlerResult = tacheHandler.execute(flow) {
         *      2.1如果不能派单: if(handlerResult == null), 那么处理如下：{
         *          2.1.1 根据tacheId删除相应的环节数据。
         *          2.1.2 返回NULL;
         *      }
         *      2.2 如果要求回退:if(handlerResult == "BCK")，那么处理如下：{
         *          2.2.1 查询该环节实例的上一个环节实例oldFlow.
         *          2.2.2 修改老环节实例的状态为未处理。
         *          2.2.3 删除当前环节实例信息. 2.2.4 返回当老环节实例。
         *      }
         *      2.3其他情况直接返回handlerRestul（是为深度优先创建的环节实例, 例如自动处理该环节）: return handlerResult;
         * }
         */
        String tacheTypeId = flow.getTacheTypeId();

        String handlerResult = KiteStringUtils.EMPTY;
        try {
            TacheHandler tachehandler = FlowEngine.getTachTypeHandle(tacheTypeId);
            if (tachehandler == null) {
                tachehandler = new DefaultTacheHandler();
            }
            handlerResult = tachehandler.perform(flow); //流程环节
        }
        catch (Exception e) {
            logger.error(ErrorConsts.SYSTEM_ERROR.getErrorCode(), e, e.getMessage());
            ErrorConsts.ORDER_FLOW_ERROR_TACHE_HANDLER_EXECUTION_ERROR.throwOut(flow.getFlowId(), tacheTypeId);
        }

        // 根据环节处理器结果判断
        if (KiteStringUtils.isEmpty(handlerResult)) {
            // 不能派单
            // 删除流程实例中指定tache_id的数据
            FlowEngine.deleteFlow(flow);
            return null;
        }
        else if (handlerResult.endsWith(OrderFlowConsts.TACHE_HANDLE_RESULT_BACK)) {
            // 要求回退
            String oldTacheId = flow.getOldTacheId();
            String tacheId = flow.getTacheId();
            if (oldTacheId.equals(tacheId)) {
                // 如果新、老环节实例标识相同，应该是流程的第一个环节，不能删除。会不会陷入死循环？？？
                return tacheId;
            }
            else {
                // 将上一环节实例数据更新
                FlowMapper flowMapper = SpringUtil.getBean(FlowMapper.class);
                Flow oldFLow = flowMapper.selectByPrimaryKey(oldTacheId);
                oldFLow.setStatus(OrderFlowConsts.FLOW_STATUS_UNPROCESS);
                Example flowExample = new Example(Flow.class);
                flowExample.createCriteria()
                        .andEqualTo("tacheId", oldFLow.getTacheId())
                        .andEqualTo("custId", oldFLow.getCustId());
                flowMapper.updateByExample(oldFLow, flowExample);

                // 删除流程实例中当前tache_id的数据
                // 删除当前环节实例数据
                flowMapper.delete(flow);

                // 返回流程实例的上一环节实例ID
                return oldTacheId;
            }
        }
        // 其它情况直接返回handlerResult
        return handlerResult;
    }


    /**
     * 提交一个环节。
     *
     * @param flow：一个完整的flow数据
     * @return
     */
    public static String commit(Flow flow) {
        /*
         * 1.修改当前环节的状态为完成的，状态时间为当前时间。 2.根据tacheTypeId得到所有的后续环节nextTacheTypeIds= tacheTypeIdMap.get(tacheTypeId);
         * 3.如果nextTacheTypeIds不为空，那么处理如下：for(nextTacheTypeId in nextTacheTypeIds) { String nextTacheId =
         * createTache(newFlow: 老环节old_tache_id为flow.tache_id); return if(nextTacheId不为NULL), return nextTacheId; }
         * 4.如果nextTacheTypeIds为空，那么结束流程： 把所有的flow信息拷贝到l_flow表中(历史表？);
         */
        String tacheId = flow.getTacheId();
        String oldTacheId = tacheId;
        String flowTypeId = flow.getFlowTypeId();
        String tacheTypeId = flow.getTacheTypeId();
        String tacheResult = flow.getResult();
        List<String> nextTacheTypeIds = Lists.newArrayList();
        String nextTacheId = "";
        // 设置当前环节的状态为完成
        flow.setStatus(OrderFlowConsts.FLOW_STATUS_PROCESSED);
        flow.setStatusDate(LocalDateTime.now());
        flow.setDealState(OrderFlowConsts.FLOW_DEAL_STATE_PROCESSED);
        flow.setDealDate(LocalDateTime.now());
        FlowMapper flowMapper = SpringUtil.getBean(FlowMapper.class);
        Example flowExample = new Example(Flow.class);
        flowExample.createCriteria()
                .andEqualTo("tacheId", flow.getTacheId())
                .andEqualTo("custId", flow.getCustId());
        flowMapper.updateByExample(flow, flowExample);

        Map<String, List<String>> hmTacheTypeMap = FlowEngine.getTacheTypeMap(flowTypeId);
        StringBuilder tacheKey = new StringBuilder().append(tacheTypeId).append("|").append(tacheResult);

        for (Map.Entry<String, List<String>> entry : hmTacheTypeMap.entrySet()) {
            if (KiteStringUtils.equals(entry.getKey(), tacheKey)) {
                nextTacheTypeIds = entry.getValue();
                break;
            }
        }

        if (KiteListUtils.isEmpty(nextTacheTypeIds)) {

            //如果不存在下一环节，归档；
            // 保存到历史表l_flow
            LFlowMapper lFlowMapper = SpringUtil.getBean(LFlowMapper.class);

            flowExample.clear();
            flowExample.createCriteria().andEqualTo("flowId", flow.getFlowId()).andEqualTo("custId", flow.getCustId());
            List<Flow> flows = flowMapper.selectByExample(flowExample);

            List<LFlow> lFlows = KiteBeanUtils.copyPropsForList(flows, LFlow.class);
            lFlowMapper.insertBatch(lFlows);

            // 删除flow表中数据
            flowMapper.deleteBatchById(flows);
            //
            return null;
        }

        // nextTacheTypeIds不为空,生成后续的流程环节
        for (int i = 0; i < nextTacheTypeIds.size(); i++) {
            tacheTypeId = (String) nextTacheTypeIds.get(i);

            Flow newFlow = FlowEngine.newFlow(flow.getFlowTypeId(), tacheTypeId, flow.getCustId());
            newFlow.setOldTacheId(oldTacheId);
            newFlow.setFlowId(flow.getFlowId());
            newFlow.setStaffId(flow.getStaffId());
            newFlow.setInstType(flow.getInstType());
            newFlow.setInstId(flow.getInstId());
            newFlow.setCustId(flow.getCustId());

            // 创建流程环节实例，并把创建的流程环节实例ID记录到oldTacheId
            nextTacheId = FlowEngine.createTache(newFlow);
            oldTacheId = nextTacheId;
        }
        return nextTacheId;
    }

    /***
     * 获取环节处理器实例
     * @param tacheTypeId
     * @return
     */
    public static TacheHandler getTachTypeHandle(String tacheTypeId) {

        TacheType tacheType = TacheTypeCache.get(tacheTypeId);
        String handleClass = tacheType.getKiteHandleClass();
        if (KiteStringUtils.isEmpty(handleClass)) {
            return null;
        }

        TacheHandler tacheHandler = null;

        try {
            tacheHandler = ClazzUtil.newInstanceFromClassName(handleClass);
        }
        catch (ClassNotFoundException e) {
            // 可能是数据库配置修改了，容错一次
            TacheTypeCache.delete(tacheTypeId);
            tacheType = TacheTypeCache.get(tacheTypeId);
            handleClass = Optional.ofNullable(tacheType).map(TacheType::getKiteHandleClass).orElse("");
            if (StringUtils.isBlank(handleClass)) {
                return null;
            }

            try {
                tacheHandler = ClazzUtil.newInstanceFromClassName(handleClass);
            }
            catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
                ErrorConsts.CLASS_NOT_FOUND.throwOut(ex);
            }
        }
        catch (InstantiationException | IllegalAccessException e) {
            ErrorConsts.CLASS_NOT_FOUND.throwOut(e);
        }

        return tacheHandler;
    }


    public static void deleteFlow(Flow flow) {
        FlowMapper flowMapper = SpringUtil.getBean(FlowMapper.class);
        Example example = new Example(Flow.class);
        example.createCriteria().andEqualTo("flowId", flow.getFlowId());
        flowMapper.deleteByExample(example);
    }

    /***
     * Description: <br>
     * new一个Flow对象；
     * @author yu.baocheng@iwhalecloud.com<br>
     * @taskId <br>
     * @CreateDate 2020/3/3 12:27 上午 <br>
     * @since ZSmart_BSS_COC_R3.1<br>
     * @param flowTypeId:
     * @param tacheTypeId:
     * @return: com.iwhalecloud.bss.kite.dataservice.entity.flow.Flow
     */
    public static Flow newFlow(String flowTypeId, String tacheTypeId, String custId) {
        Flow flow = new Flow();
        flow.setFlowTypeId(flowTypeId);
        flow.setTacheTypeId(tacheTypeId);
        flow.setStatus(OrderFlowConsts.FLOW_STATUS_UNPROCESS);
        flow.setStatusReasonCode(OrderFlowConsts.FLOW_STATUS_REASON_CODE_NOCAUSE);
        //其它字段设置为空字符串。
        flow.setTacheId("");
        flow.setOldTacheId("");
        flow.setFlowId("");
        flow.setStatusReasonMessage("");
        flow.setResultMessage("");
        flow.setStaffId("");
        flow.setDealDate(LocalDateTime.now());
        flow.setDealState("");
        flow.setInstType("");
        flow.setInstId("");
        flow.setOrderItemProp("");
        flow.setCustId(custId);
        return flow;
    }

    public static Map<String, List<String>> getTacheTypeMap(String flowTypeId) {
        Map<String, List<String>> tacheTypeMap = Maps.newHashMap();
        /*1.环节转移表: key = tacheId+|+result, value为该环节的后续环节列表List of TacheType */
        List<TacheTypeRelation> tacheTypeRelations = TacheTypeRelationCache.get(flowTypeId);

        for (TacheTypeRelation relation : tacheTypeRelations) {
            List<String> objToTache = tacheTypeMap.get(relation.getFromTacheTypeId() + "|" + relation.getResult());
            List<String> listToTache;
            if (objToTache == null) {
                listToTache = Lists.newArrayList();
            }
            else {
                listToTache =  objToTache;
            }
            listToTache.add(relation.getToTacheTypeId());
            tacheTypeMap.put(relation.getFromTacheTypeId() + "|" + relation.getResult(), listToTache);
        }
        return tacheTypeMap;
    }


    public static String createTache(Flow flow) {
        /*
         * 1.在flow 表中插入相应的环节信息。 2.调用相应的处理器: return invokeTacheHandler(flow);
         */
        String tacheId = FlowEngine.generateTacheId();
        // 判断是否第一个流程，如果是要将old_tache_id赋值为本次获取到的tache_id。否则在调用前应该将上一笔流程环节实例的tache_id保存到old_tache_id
        if (KiteStringUtils.isEmpty(flow.getOldTacheId())) {
            flow.setOldTacheId(tacheId);
        }
        // 保存新的tache_id
        flow.setTacheId(tacheId);
        // 更新flow对象数据

        flow.setCreateDate(LocalDateTime.now());
        flow.setStatusDate(LocalDateTime.now());
        flow.setDealState(OrderFlowConsts.FLOW_DEAL_STATE_UNPROCESS); // 在生成环节实例时默认的处理状态都为未处理。实际的处理状态值由处理器来修改
        // 插入流程的环节实例数据
        FlowMapper flowMapper = SpringUtil.getBean(FlowMapper.class);
        flowMapper.insert(flow);
        return FlowEngine.invokeTacheHander(flow);
    }

    /**
     * 生成 Flow表的 flow_id
     * @return
     */
    public static String generateFlowId() {
        return IdUtils.generateFlowFlowId();
    }

    /**
     * 获取流程实例中环节实例标识ID（tache_id）
     * @return
     */
    public static String generateTacheId() {
        return IdUtils.generateFlowTacheId();
    }


    /**
     * 根据基础销售品订单查询接入产品实例数据。组合销售品订单不能直接关联到接入产品实例
     **/
    public static ProdInst getProdInfo(OrderItem orderItem) {
        ProdInst prodInst = null;
        if (!KiteStringUtils.isEqual(orderItem.getOfferKind(), KeyValues.OFFER_KIND_0)) {
            return null;
        }
        ProdInstMapper prodInstMapper = SpringUtil.getBean(ProdInstMapper.class);
        Example example = new Example(ProdInst.class);
        example.createCriteria().andEqualTo("compInstId", orderItem.getObjId()).andEqualTo("ownerCustId",
                orderItem.getBelongCustId());

        prodInst = prodInstMapper.selectOneByExample(example);
        if (Objects.isNull(prodInst)) {
            example.clear();
            example.createCriteria().andEqualTo("compInstId", orderItem.getObjId());
            prodInst = prodInstMapper.selectOneByExample(example);
        }

        return prodInst;
    }
}


